/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of LINAL (LINear ALgebra)
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/

#include "uhm/common.hxx"
#include "uhm/util.hxx"

#ifdef __PETSC_USE__
#include "petscksp.h"
#endif

namespace uhm {

  Int_ initialize(int argc, char **argv) {
    linal::initialize(argc, argv);

#ifdef __PETSC_USE__
    PetscErrorCode ierr;
    char help[] = "UHM interface to Petsc\n";
    ierr = PetscInitialize(&argc,
                           &argv, 
                           (char*)0, 
                           help);
    CHKERRQ(ierr);
#endif

    set_option(argc, argv);

    return true;
  }

  Int_ finalize() {
    linal::finalize();

#ifdef __PETSC_USE__
    PetscErrorCode ierr;
    ierr = PetscFinalize();CHKERRQ(ierr);
#endif
    Double_ storage, max_storage;
    storage_counter(storage, max_storage);    
    std::cout << "Time Stack      = " << time_stack_size() << "\n";
    std::cout << "Storage Counter = " << storage << "\n\n";
    std::cout << "Remark : Storage counter may not be zero when multithreading is used.\n";
    return true;
  }

  void hold() {
    std::cout << "Press [Enter] to continue ..." << std::endl;
    std::cin.get();
  }

  static Double_ s_secbase = 0.0E0;
  Double_ stopwatch() {
    struct timeval tv;
    struct timezone Tzp;
    Double_ sec;

    gettimeofday( &tv, &Tzp );

    if ( s_secbase == 0.0E0 )
      s_secbase = (Double_)tv.tv_sec;

    sec = (Double_)tv.tv_sec - s_secbase;

    return (sec + 1.0E-06*(Double_)tv.tv_usec);
  }

  static std::stack< std::pair<std::string,Double_> > s_time;
  static std::ostream *s_os = &(std::cout);
  static Int_ s_enabled = false;

  void set_time_ostream(std::ostream &os) { s_os = &os; }

  void time_begin(Int_ enabled) { s_enabled = enabled; }
  void time_end() { 
    if (s_time.size()) {
      std::cout << "Warning !! Time Stack is NOT Empty. It will be FLUSHED.\n";
    }
    
    s_enabled = false; 
    time_flush(); 
  }
  Int_ time_stack_size() { return s_time.size(); }

  void time_in(const Char_* name, Int_ level) {
    if (s_enabled >= level) {
      s_time.push(std::make_pair(std::string(name), stopwatch()));
      *s_os << "[Time Stack: " << s_time.size() << "] " << s_time.top().first << "\n";
    }
  }
  Double_ time_out(Int_ level) {
    Double_ r_val = 0.0;
    if (s_enabled >= level) {
      r_val = (stopwatch() - s_time.top().second);
      *s_os << "[Time: "<< std::scientific << r_val << "] " 
            << s_time.top().first << "\n";
      s_time.pop();
    }
    return r_val;
  }
  void time_flush() {
    while(!s_time.empty()) 
      s_time.pop();
  }

  static Double_ s_storage = 0.0, s_max_storage = 0.0;

  void storage_counter_reset() {
    s_storage = 0.0; s_max_storage = 0.0;
  }
  void storage_counter(Double_ &storage, Double_ &max_storage) {
    storage = s_storage; max_storage = s_max_storage;
  }
  void storage_counter_in(Double_ storage) {
#pragma omp atomic
    s_storage += storage;

    s_max_storage = (s_max_storage < s_storage ? s_storage : s_max_storage);
  }
  void storage_counter_out(Double_ storage) {
#pragmaomp atomic
    s_storage -= storage;
  }
  
}
